module.exports = app => {
  const express = require('express')
  // 整体引入需要的模块 jsonwebtoken，http-assert
  const jwt = require('jsonwebtoken')
  const assert = require('http-assert')
  const AdminUser = require('../../models/AdminUser') //整体引入模型

  const router = express.Router({
      mergeParams: true     //合并参数，最后那行  app.use('/admin/api/rest/:resource', router)能获取到参数，但是const model = require('../../models/:resource')不能获取到参数resource
  })


  // 1234 代表新建到修改的
  // 1：创建资源 4.更新资源，2,资源列表 4.资源详情 5.删除资源

  // 1
  // (不存在id：新建)(router.js中一个组件有两个路径，props：true为传递id到组件中)categoryEdit组件点击保存,添加内容到category接口


  router.post('/', async (req, res) => {
    // 本来是const Category = require('../../models/Category.js'), 
    // const model = await Category.create(req.body)
    const model = await req.Model.create(req.body)
    res.send(model)
  })


  // 4
  // (存在id：编辑)categoryEdit组件点击保存,修改category接口的内容

  router.put('/:id', async (req, res) => {
    const model = await req.Model.findByIdAndUpdate(req.params.id, req.body)
    res.send(model)
  })


  // 2
  // categoryList组件在进入分类列表前获取数据(限制10条)，然后呈现出来


  router.get('/', async (req, res) => {
    // 原本只有一个分类（没上级分类）
    // const items = await Category.find().limit(10)
    // 添加上级分类：populate('parent')，关联parent,在network中parent由一个id值变成一个对象，对象里面有{ name: "aa"，parent: {_id: "5dcd188e093d31fc216abff8", name: "新闻", __v: 0}，__v: 0，_id: "5dcd1d784d4b563a8a5d33fa"} ,parent为新闻，name为aa,可以获取parent.name，在前端展示

    // 变成通用后，populate('parent')里面的参数不一定是parent，
    // queryOptions是参数，如果modelName是Category，这样populate加上parents，区分这是分类组件，实现多级分类的效果，其他组件则不需要
    // 实现通用性加扩展性的效果
    const queryOptions = {} 
    if (req.Model.modelName === 'Category') {
        queryOptions.populate = 'parent'
    }
    const items = await req.Model.find().setOptions(queryOptions).limit(100)
    res.send(items)
  })


  // 3
  // (存在id的情况下:编辑)categoryEdit组件获取具体id对应的内容,方便修改


  router.get('/:id', async (req, res) => {
    const model = await req.Model.findById(req.params.id)
    res.send(model)
  })


  //5
  // categoryList组件删除具体id对应的内容 :id为categoryList中的props=:_id


  router.delete('/:id',async (req,res) => {
    await req.Model.findByIdAndDelete(req.params.id)
    res.send({
      success: true
    })
  })

  // 登录校验中间件
  const authMiddleware = require('../../middleware/auth')
  const resourceMiddleware = require('../../middleware/resource')

  // app对象在3000端口运行，router路由跳转到/admin/api接口中，上面的一系列行为都是增删查改行为 post delete get put， Category是指分类这个接口
  // 通用接口rest前缀加动态参数:resource (之前为categories等)
  app.use('/admin/api/rest/:resource',authMiddleware(),resourceMiddleware(),router)  //插入两个中间件函数req,next ,req,next,最后再进行路由操作，加括号()表示调用函数（返回函数，这是闭包），自己可按照要求添加参数


  // 这是一个中间件:处理上传数据
  const multer = require('multer')
  // 当前文件所在的文件夹位置的上上文件夹（server）下的upload文件夹，创建upload文件夹
  // G:\my-learning\moba\server\routes\admin + /../../uploads
  const upload = multer({dest: __dirname + '/../../uploads'})
  // 上传文件操作
  /*
  @route http;//localhost:3000/admin/api/upload
  这里不是router.post,而是app.post，不是路由跳转
  */
  app.post('/admin/api/upload', authMiddleware(),upload.single('file'), async (req,res,next) => { //上传文件前也要运行授权校验函数（尽量实现管理员权限）
      const file = req.file //file为传递数据的格式 file: {fileNmae:.....}
      file.url = `http://localhost:3000/uploads/${file.filename}` //模板字符串
      res.send(file)  //将数据传递到管理端
  })



  /*
  @route http://localhost:3000/admin/api/login
  POST
  */
  app.post('/admin/api/login',async (req,res,next) => {
    const { username,password } = req.body// 接收用户名，密码(两个属性) {username:.... , password:.....}

    /*
    ** 1.根据用户名找用户
    ** 2.校验密码
    ** 3.返回token，发送token到客户端admin
    */

    //1.根据用户名找用户
    // 上面已经引入模型
    const user = await AdminUser.findOne({username}).select('+password')  //查找一个数据,{username}是简写，实际上是{username:username}，对象属性和属性值相同可简写,
    // 默认不取出密码，但是登录需要比较密码，select('+password')可以顺带把符合username的密码也一并去除，+号表示password需要取出
    // console.log(user)
    assert(user, 422, {message:'用户不存在'}) //使用assert,如果user（上面）不存在，则422状态码报错，消息提示用户不存在

    

    // 2.校验密码
    const isValid = require('bcrypt').compareSync(password,user.password) //返回布尔值 true or false
    //bcrypt会把密码生成一串散列，hashSync(),现在比较用户输入的密码password（req.body中）和 encrypted加密的 user.password（存储在管理后台中AdminUser），
    // 原理大概（不确定，因为平常的字符密码可以转换为不同的散列）是将管理后台加密的user.password（一串散列）先转换为平常的字符密码，再与输入的密码（也是平常的字符密码）进行比较，将结果赋值给isValid
    assert(isValid, 422, {message:'密码错误'})

    // 3.返回token
    // 上面已引入
    const token = jwt.sign({id: user._id}, app.get('secret')) //jwt的sign签名方法，用户的唯一id 作为payload （第一个参数），secret秘钥（第二个参数）（最好看文章了解）
    // app.get()，如果只有一个参数，则是获取配置
    // token原理，建议看文章，简单说是把数据生成一串散列从服务端发送到客户端
    res.send({ token }) //token是属性，服务端所有操作的主体是对象中的属性，这里服务端发送token到客户端
  })


  // 错误处理函数 (如果注释，则客户端http.js不能获取err.message)，看admin/src/http.js 42-60
  // 多了一个参数err
  app.use(async (err,req,res,next) => {
    // console.log(err)
    // 和之前上面差不多 res.status(422).send({ message: '密码错误'})
    res.status(err.statusCode || 500).send({    //获取错误的状态码，如果获取不到，直接为500,（反正都会报错），然后发送报错信息到客户端
      message: err.message
    })
  })
}